package model;

import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.junit.experimental.theories.PotentialAssignment;

import solver.moves.AbstractMove;


import command.Invoker;

public class SolverEnvironment {
		public final Problem problem;
		public final Solution initial_solution;
		public final Random random;
		public final Invoker invoker;
		public final IncrementalValidator iv;
		public final IncrementalCostCalculator icc;
		public final EnhancedCostEvaluator ece;
		
		private Solution current_solution;
		private Solution potential_solution;
		
		List<AbstractMove> moves = new ArrayList<AbstractMove>();
		
		//PA Added for transient capacity elimination
		Set<Integer>[] processesIllegalMachines;
		
		public SolverEnvironment(Problem problem, Solution initialSolution) {
		  this(problem,initialSolution, initialSolution.copy());
		}
		
		public SolverEnvironment(Problem problem, Solution initialSolution,
				Solution currentSolution) {			
			this(problem,initialSolution,currentSolution, 
					new Random(Problem.RANDOM_SEED),
					new Invoker(),
					new IncrementalValidator(),
					(problem.getBalanceObjectives().size() > 0)?
							new IncrementalCostCalculator():
							new IncrementalCostCalculatorNoBalance(),
					new EnhancedCostEvaluator()
					);
		}
		
		public SolverEnvironment(Problem problem, Solution initialSolution, Solution currentSolution, 
				Random random, Invoker invoker, IncrementalValidator iv, IncrementalCostCalculator icc,
				EnhancedCostEvaluator ece) {
            this.problem = problem;
            this.random = random;
            this.invoker = invoker;	
            this.initial_solution = initialSolution;
			initInitialSolution();
						
			setCurrentSolution(currentSolution);
			
			this.iv = iv;
			initIncrementalValidator();
			
			this.icc = icc;
			initCostCalculator();
			
			this.ece = ece;
			initFullValidator();
			
			setPotentialSolution(currentSolution);
		}

		public boolean areAllProcessesAssigned() {
			return current_solution.areAllProcessesAssigned();
		}

		public final IncrementalCostCalculator getCostCalculator() {
			return icc;
		}
		
		public final Solution getCurrentSolution() {
			return current_solution;
		}
		
		public final EnhancedCostEvaluator getFullValidator() {
			return ece;
		}
		
		public final Solution getInitialSolution() {
			return initial_solution;
		}
		
		public final Invoker getInvoker() {
			return invoker;
		}
		
		public final Solution getPotentialSolution() {
			return potential_solution;
		}

		public final Problem getProblem() {
			return problem;
		}

		public final Random getRandom() {
			return random;
		}

		public final IncrementalValidator getValidator() {
			return iv;
		}

		private void initCostCalculator() {
			icc.setEnv(this);
		}

		private void initFullValidator() {
			ece.setEnv(this);
		}

		private void initIncrementalValidator() {
			iv.setEnv(this);
		}

		private void initInitialSolution() {
//			System.err.println("Attempt to init a new initial solution");
			if(ece!=null)
				ece.init();
			if(icc!=null)
				icc.updateStructuresOnInitialSolutionChange();
			
			createIllegalMachines();
		}

		private void createIllegalMachines() {
			processesIllegalMachines = new Set[problem.P];
			
			for (int p = 0; p < processesIllegalMachines.length; p++) {
				processesIllegalMachines[p] = new HashSet<Integer>();
			}
			
			for(int i=0;i<problem.TRE.length;i++){
				int r= problem.TRE[i];
				for(int p=0;p<problem.P;p++){
					long u = problem.getR(p, r);
					for (int m = 0; m < problem.M; m++){
						if(m==initial_solution.processes[p])
							continue;
						if(initial_solution.usage[m][r]+u>problem.C[m][r])
							processesIllegalMachines[p].add(m);
					}
				}
			}
			
//			System.out.println("Illegal Machines for processes");
//			for(int p=0;p<problem.P;p++)
//				System.out.println(p+"["+processesIllegalMachines[p].size()+
//						"/"+ problem.M+"]:"+processesIllegalMachines[p]);
		}

		public void registerMove(AbstractMove m){
			moves.add(m);
		}

		public void setCurrentSolution(Solution s) {
			if(current_solution!=s){
//				System.err.println("Attempt to set a new current solution");
				current_solution = s;
				setPotentialSolution(current_solution);
			}
			else{
//				System.err.println("Attempt to set the same solution");
			}
		}

		public void setPotentialSolution(Solution potential_solution) {
//			System.err.println("Attempt to set a new potential solution");
			this.potential_solution = potential_solution;
			potential_solution.setEnv(this);
			if(ece!=null)
				ece.updateStructuresOnSolutionChange();
			if(iv!=null)
				iv.updateStructuresOnSolutionChange();
			if(icc!=null)
				icc.updateStructuresOnSolutionChange();
		}

		public Set<Integer>[] getProcessesIllegalMachines() {
			return processesIllegalMachines;
		}

		public final Set<Integer> getIllegalMachines(int p) {
			return processesIllegalMachines[p];
		}
}
